﻿// ***********************************************************************
// Assembly         : Tx.OCC
// Author           : tianteng
// Created          : 09-06-2019
//
// Last Modified By : tianteng
// Last Modified On : 09-09-2019
// ***********************************************************************
// <copyright file="OccBRepBuilderEx.cs" company="北京腾雪科技有限责任公司">
//     Copyright © 北京腾雪科技有限责任公司 2022
// </copyright>
// <summary></summary>
// ***********************************************************************
using System;
using System.Collections.Generic;
using System.Linq;
using Occt;

namespace Tx.OCC
{
    /// <summary>
    /// Class OccBRepBuilderEx.
    /// </summary>
    public static class OccBRepBuilderEx
    {
        /// <summary>
        /// Creates the specified shapes.
        /// </summary>
        /// <param name="shapes">The shapes.</param>
        /// <returns>Compound.</returns>
        public static TopoDS_Compound MakeCompound(this IEnumerable<TopoDS_Shape> shapes)
        {
            var builder = new BRep_Builder();
            builder.MakeCompound(out var compound);
            foreach (var shape in shapes)
            {
                builder.Add(compound, shape);
            }
            return compound;
        }

        /// <summary>
        /// Creates the specified shapes.
        /// </summary>
        /// <param name="shapes">The shapes.</param>
        /// <returns>Compound.</returns>
        public static TopoDS_Compound MakeCompound(params TopoDS_Shape[] shapes)
        {
            return MakeCompound(shapes);
        }
        /// <summary>
        /// Adds the line edge.
        /// </summary>
        /// <param name="builder">The builder.</param>
        /// <param name="vertex1">The vertex1.</param>
        /// <param name="vertex2">The vertex2.</param>
        /// <param name="precision">The precision.</param>
        /// <returns>TopoDS_Edge.</returns>
        public static TopoDS_Edge AddLineEdge(this BRep_Builder builder, TopoDS_Vertex vertex1, TopoDS_Vertex vertex2, double precision)
        {
            gp_Pnt pt1 = vertex1.ToPnt(), pt2 = vertex2.ToPnt();
            var dir = new gp_Dir(pt1.XYZ - pt2.XYZ);
            var line = new Geom_Line(vertex1.ToPnt(), dir);
            builder.MakeEdge(out var edge, line, precision);
            vertex1.Orientation = TopAbs_Orientation.TopAbs_FORWARD;
            vertex2.Orientation = TopAbs_Orientation.TopAbs_REVERSED;
            builder.Add(edge, vertex1);
            builder.Add(edge, vertex2);
            builder.UpdateVertex(vertex1, 0, edge, precision);
            builder.UpdateVertex(vertex2, pt1.Distance(pt2), edge, precision);
            return edge;
        }

        /// <summary>
        /// Adds the circle edge.
        /// </summary>
        /// <param name="builder">The builder.</param>
        /// <param name="vertex1">The vertex1.</param>
        /// <param name="vertex2">The vertex2.</param>
        /// <param name="precision">The precision.</param>
        /// <returns>TopoDS_Edge.</returns>
        public static TopoDS_Edge AddCircleEdge(this BRep_Builder builder, TopoDS_Vertex vertex1, TopoDS_Vertex vertex2, double precision)
        {
            gp_Pnt pt1 = vertex1.ToPnt(), pt2 = vertex2.ToPnt();
            var circle = new Geom_Circle(new gp_Ax2(pt1.Middle(pt2), new gp_Dir(0, 1, 0), new gp_Dir(-1, 0, 0)), pt1.Distance(pt2) * 0.5);
            builder.MakeEdge(out var edge, circle, precision);
            vertex1.Orientation = TopAbs_Orientation.TopAbs_FORWARD;
            vertex2.Orientation = TopAbs_Orientation.TopAbs_REVERSED;
            builder.Add(edge, vertex1);
            builder.Add(edge, vertex2);
            builder.UpdateVertex(vertex1, 0, edge, precision);
            builder.UpdateVertex(vertex2, Math.PI, edge, precision);
            return edge;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="face"></param>
        /// <param name="dir"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static TopoDS_Shape MakePrism(TopoDS_Face face, gp_Dir dir, double length)
        {
            return new BRepPrimAPI_MakePrism(face, new gp_Vec(dir.XYZ.Multiplied(length))).Shape;
        }

        /// <summary>
        /// Makes the prism.
        /// </summary>
        /// <param name="verts">The verts.</param>
        /// <param name="dir">The dir.</param>
        /// <param name="length">The length.</param>
        /// <returns>Shape.</returns>
        public static TopoDS_Shape MakePrism(IList<gp_Pnt> verts, gp_Dir dir, double length)
        {
            var pln = new gp_Pln(verts.First(), dir);
            var wire = MakeWire(verts, true);
            var face = MakeFace(pln, wire);
            return MakePrism(face, dir, length);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="circ"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static TopoDS_Shape MakePrism(gp_Circ circ, double length)
        {
            var pln = new gp_Pln(circ.Location, circ.Axis.Direction);
            var wire = MakeWire(circ);
            var face = MakeFace(pln, wire);
            return MakePrism(face, circ.Axis.Direction, length);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pln"></param>
        /// <param name="wire"></param>
        /// <returns></returns>
        public static TopoDS_Face MakeFace(gp_Pln pln, TopoDS_Wire wire)
        {
            var face = new BRepBuilderAPI_MakeFace(pln, wire).Face;
            return face;
        }

        /// <summary>
        ///     Creates the specified verts.
        /// </summary>
        /// <param name="verts">The verts.</param>
        /// <param name="bClose">if set to <c>true</c> [b close].</param>
        /// <returns>Wire.</returns>
        public static TopoDS_Wire MakeWire(IList<gp_Pnt> verts, bool bClose = false)
        {
            var makeWire = new BRepBuilderAPI_MakeWire();
            foreach (var edge in MakeEdge(verts, bClose))
            {
                makeWire.Add(edge);
            }
            return makeWire.Wire;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="circ"></param>
        /// <returns></returns>
        public static TopoDS_Wire MakeWire(gp_Circ circ)
        {
            return MakeWire(MakeEdge(circ));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="edge"></param>
        /// <returns></returns>
        public static TopoDS_Wire MakeWire(TopoDS_Edge edge)
        {
            var makeWire = new BRepBuilderAPI_MakeWire();
            makeWire.Add(edge);
            return makeWire.Wire;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="circ"></param>
        /// <returns></returns>
        public static TopoDS_Edge MakeEdge(gp_Circ circ)
        {
            return new BRepBuilderAPI_MakeEdge(circ).Edge;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="verts"></param>
        /// <param name="bClose"></param>
        /// <returns></returns>
        public static IEnumerable<TopoDS_Edge> MakeEdge(IList<gp_Pnt> verts, bool bClose = false)
        {
            foreach (var tuple in verts.AdjacentTwo(bClose))
            {
                yield return new BRepBuilderAPI_MakeEdge(tuple.Item1, tuple.Item2).Edge;
            }
        }
    }
}
